﻿namespace MailChimp

open System
open MailChimp.Types

//==================================================================================
//  MCApi class
//==================================================================================

[<Sealed>]
type MCApi(apiKey: string, secure: bool) =
    inherit ApiBase(apiKey, secure)

    override x.UserAgent = "MCAPI.NET/1.3"
    override x.ApiUrl = "http://{dc}.api.mailchimp.com/1.3/?output={fmt}&method={method}"
    override x.OutputFormat = Format.xml
    override x.KeyParam = "apikey"


module MCHelper =

    let deserialize<'T> x =
        try deserializeXml<'T> x, x
        with | ex -> deserializeFail ex x

    let prepare x =
        let o = try prepareXml x with | ex -> transformFail ex x
        let e = try deserializeXml<MCError> o |> Some with | _ -> None
        match e with
        | None -> o
        | Some e -> raise (new MCException(e, null, x))


open MCHelper

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MCApi =

    let callServerRaw (x: MCApi) name p =
        let st, d = x.CallServer(WebMethod.Post, name, None, p)
        match st with
        | httpst.OK -> d
        | c -> callServerFail null (int c) d

    let callServer<'T> (x: MCApi) name p =
        callServerRaw x name p |> prepare |> deserialize<'T> |> fst

    let callServerFn<'T> (x: MCApi) name p f: 'T =
        callServerRaw x name p |> prepare |> deserialize<'T> |> f

    //------------------------------------------------------------------------------
    //  (*) Email Errors
    //------------------------------------------------------------------------------

    let getEmailErrors nerr x =
        if nerr > mcnull 0 then
            let e, _ = deserialize<EmailErrors> x
            e.Errors |> Seq.filter(fun z -> not (isNullOrEmpty z.Error)) |> MCList.ofSeq
        else MCList()

    //==================================================================================
    //  Security related methods
    //==================================================================================

    module Security =

        open MailChimp.Types.Security

        let apikeys (x: MCApi) (user: string) (pwd: string) (expired: bool opt) =
            let p = Input()
            p.["username"] <- user
            p.["password"] <- pwd
            p.["expired"] <- expired
            callServer<MCList<ApiKey>> x "apikeys" (Some p)

        let apikeyAdd (x: MCApi) (user: string) (pwd: string) =
            let p = Input()
            p.["username"] <- user
            p.["password"] <- pwd
            callServer<string> x "apikeyAdd" (Some p)

        let apikeyExpire (x: MCApi) (user: string) (pwd: string) =
            let p = Input()
            p.["username"] <- user
            p.["password"] <- pwd
            callServer<bool> x "apikeyExpire" (Some p)

    //==================================================================================
    //  Helper methods
    //==================================================================================

    module Helper =

        open MailChimp.Types.Helper
        open MailChimp.Types.Campaign

        let ping (x: MCApi) = callServer<string> x "ping" None

        let getAccountDetails (x: MCApi) (exclude: DetailType[]) =
            let p = Input()
            p.["exclude"] <- exclude
            callServer<AccountDetails> x "getAccountDetails" (Some p)

        let getVerifiedDomains (x: MCApi) =
            callServer<MCList<Domain>> x "getVerifiedDomains" None

        let chimpChatter (x: MCApi) =
            callServer<MCList<ChimpChatterItem>> x "chimpChatter" None

        let listsForEmail (x: MCApi) (email: string) =
            let p = Input()
            p.["email_address"] <- email
            callServer<MCList<string>> x "listsForEmail" (Some p)

        let campaignsForEmail (x: MCApi) (email: string) (listID: string opt) =
            let o = Input()
            o.["list_id"] <- listID
            o.["verbose"] <- false
            let p = Input()
            p.["email_address"] <- email
            p.["options"] <- o
            callServer<MCList<string>> x "campaignsForEmail" (Some p)

        let campaignsForEmail2 (x: MCApi) (email: string) (listID: string opt) =
            let o = Input()
            o.["list_id"] <- listID
            o.["verbose"] <- true
            let p = Input()
            p.["email_address"] <- email
            p.["options"] <- o
            callServer<MCList<CampaignsForEmailItem>> x "campaignsForEmail" (Some p)

        let inlineCss (x: MCApi) (html: string) (stripCss: bool opt) =
            let p = Input()
            p.["html"] <- html
            p.["strip_css"] <- stripCss
            callServer<string> x "inlineCss" (Some p)

        let generateText (x: MCApi) (contentType: Campaign.ContentType) (content: obj) =
            let p = Input()
            p.["type"] <- contentType
            p.["content"] <- content
            callServer<string> x "generateText" (Some p)

        let searchMembers (x: MCApi) (query: string) (listID: string opt) (offset: int opt) =
            let p = Input()
            p.["query"] <- query
            p.["id"] <- listID
            p.["offset"] <- offset
            callServer<SearchMembersResult> x "searchMembers" (Some p)

        let searchCampaigns (x: MCApi) (query: string) (offset: int opt)
            (snipRange: (string * string) opt) =
            let p = Input()
            p.["query"] <- query
            p.["offset"] <- offset
            match snipRange with
            | Some (s, e) ->
                p.["snip_start"] <- s
                p.["snip_end"] <- e
            | _ -> ()
            callServer<SearchCampaignsResult> x "searchCampaigns" (Some p)

    //==================================================================================
    //  List related methods
    //==================================================================================

    module List =

        open MailChimp.Types.List

        //------------------------------------------------------------------------------
        //  (*) Lists
        //------------------------------------------------------------------------------

        let lists (x: MCApi) (filters: Filter opt) (start: int opt) (limit: int opt)
            (sortField: SortField opt) (sortDir: SortDirection opt) =
            let p = Input()
            p.["filters"] <- filters
            p.["start"] <- start
            p.["limit"] <- limit
            p.["sort_field"] <- sortField
            p.["sort_dir"] <- sortDir
            callServer<Lists> x "lists" (Some p)

        //------------------------------------------------------------------------------
        //  (*) Activity/Growth History
        //------------------------------------------------------------------------------

        let activity (x: MCApi) (listID: string) =
            let p = Input()
            p.["id"] <- listID
            callServer<MCList<ActivityItem>> x "listActivity" (Some p)

        let locations (x: MCApi) (listID: string) =
            let p = Input()
            p.["id"] <- listID
            callServer<MCList<Location>> x "listLocations" (Some p)

        let abuseReports (x: MCApi) (listID: string) (start: int opt)
            (limit: int opt) (since: DateTime opt) =
            let p = Input()
            p.["id"] <- listID
            p.["start"] <- start
            p.["limit"] <- limit
            p.["since"] <- since
            callServer<AbuseReports> x "listAbuseReports" (Some p)

        let growthHistory (x: MCApi) (listID: string) =
            let p = Input()
            p.["id"] <- listID
            callServer<MCList<GrowthHistoryItem>> x "listGrowthHistory" (Some p)

        let clients (x: MCApi) (listID: string) =
            let p = Input()
            p.["id"] <- listID
            callServer<Clients> x "listClients" (Some p)

        //------------------------------------------------------------------------------
        //  (*) Interest Groupings/Groups
        //------------------------------------------------------------------------------

        let interestGroupings (x: MCApi) (listID: string) =
            let p = Input()
            p.["id"] <- listID
            callServer<MCList<InterestGrouping>> x "listInterestGroupings" (Some p)

        let interestGroupingAdd (x: MCApi) (listID: string) (name: string)
            (groupingType: GroupingType) (groups: string seq) =
            let p = Input()
            p.["id"] <- listID
            p.["name"] <- name
            p.["type"] <- groupingType
            p.["groups"] <- groups
            callServer<int> x "listInterestGroupingAdd" (Some p)

        let interestGroupingDel (x: MCApi) (groupingID: int)  =
            let p = Input()
            p.["grouping_id"] <- groupingID
            callServer<bool> x "listInterestGroupingDel" (Some p)

        let interestGroupingUpdate (x: MCApi) (groupingID: int) (value: obj)  =
            let p = Input()
            let n = match value with
                    | :? string -> "name"
                    | :? GroupingType -> "type"
                    | _ -> invalidArg "value" "must be String or GroupingType"
            p.["grouping_id"] <- groupingID
            p.["name"] <- n
            p.["value"] <- value
            callServer<bool> x "listInterestGroupingUpdate" (Some p)

        let private interestGroupOp (x: MCApi) op (listID: string) (name: string)
            (groupingID: int opt) =
            let p = Input()
            let n = "listInterestGroup" + op
            p.["id"] <- listID
            p.["group_name"] <- name
            p.["grouping_id"] <- groupingID
            callServer<bool> x n (Some p)

        let interestGroupAdd x listID name groupingID =
            interestGroupOp x "Add" listID name groupingID

        let interestGroupDel x listID name groupingID =
            interestGroupOp x "Del" listID name groupingID

        let interestGroupUpdate (x: MCApi) (listID: string) (oldName: string)
            (newName: string) (groupingID: int opt) =
            let p = Input()
            p.["id"] <- listID
            p.["old_name"] <- oldName
            p.["new_name"] <- newName
            p.["grouping_id"] <- groupingID
            callServer<bool> x "listInterestGroupUpdate" (Some p)

        //------------------------------------------------------------------------------
        //  (*) Static Segments
        //------------------------------------------------------------------------------

        let staticSegments (x: MCApi) (listID: string) =
            let p = Input()
            p.["id"] <- listID
            callServer<MCList<StaticSegment>> x "listStaticSegments" (Some p)

        let staticSegmentAdd (x: MCApi) (listID: string) (name: string) =
            let p = Input()
            p.["id"] <- listID
            p.["name"] <- name
            callServer<int> x "listStaticSegmentAdd" (Some p)

        let private staticSegmentOp (x: MCApi) op (listID: string) (segID: int) =
            let p = Input()
            let n = "listStaticSegment" + op
            p.["id"] <- listID
            p.["seg_id"] <- segID
            callServer<bool> x n (Some p)

        let staticSegmentDel x listID segID = staticSegmentOp x "Del" listID segID
        let staticSegmentReset x listID segID = staticSegmentOp x "Reset" listID segID

        let private segmentMembersOp (x: MCApi) op (listID: string)
            (segID: int) (batch: string seq) =
            let p = Input()
            let n = "listStaticSegmentMembers" + op
            p.["id"] <- listID
            p.["seg_id"] <- segID
            p.["batch"] <- batch
            callServer<StaticSegmentMembersOpResult> x n (Some p)

        let staticSegmentMembersAdd x listID segID batch =
            segmentMembersOp x "Add" listID segID batch

        let staticSegmentMembersDel x listID  segID batch =
            segmentMembersOp x "Del" listID segID batch

        //------------------------------------------------------------------------------
        //  (*) Members/Info/Activity
        //------------------------------------------------------------------------------

        let members (x: MCApi) (listID: string) (status: MemberStatus) (since: DateTime opt)
            (start: int opt) (limit : int opt) (sortDir: SortDirection opt) =
            let p = Input()
            p.["id"] <- listID
            p.["status"] <- status
            p.["since"] <- since
            p.["start"] <- start
            p.["limit"] <- limit
            p.["sort_dir"] <- sortDir
            callServer<Members> x "listMembers" (Some p)

        let memberInfo (x: MCApi) (listID: string) (emails: string seq) =
            let p = Input()
            p.["id"] <- listID
            p.["email_address"] <- emails
            callServerFn<MemberInfo> x "listMemberInfo" (Some p) (fun (y, s) ->
              { y with ErrorsData = getEmailErrors y.Errors s })

        let memberActivity (x: MCApi) (listID: string) (emails: string seq) =
            let p = Input()
            p.["id"] <- listID
            p.["email_address"] <- emails
            callServerFn<MemberActivity> x "listMemberActivity" (Some p) (fun (y, s) ->
              { y with ErrorsData = getEmailErrors y.Errors s })

        //------------------------------------------------------------------------------
        //  (*) Subscribe/Unsubscribe
        //------------------------------------------------------------------------------

        let subscribe (x: MCApi) (listID: string) (email: string)
            (merges: Merges opt) (options: SubscribeOptions opt) =
            let p = Input()
            p.["id"] <- listID
            p.["email_address"] <- email
            p.["merge_vars"] <- merges
            match options with
            | None -> ()
            | Some o -> p.Merge o
            callServer<bool> x "listSubscribe" (Some p)

        let unsubscribe (x: MCApi) (listID: string) (email: string)
            (options: UnsubscribeOptions opt) =
            let p = Input()
            p.["id"] <- listID
            p.["email_address"] <- email
            match options with
            | None -> ()
            | Some o -> p.Merge o
            callServer<bool> x "listUnsubscribe" (Some p)

        let updateMember (x: MCApi) (listID: string) (email: string)
            (merges: Merges) (emailType: EmailType opt) (replaceInerests: bool opt) =
            let p = Input()
            p.["id"] <- listID
            p.["email_address"] <- email
            p.["merge_vars"] <- merges
            p.["email_type"] <- emailType
            p.["replace_interests"] <- replaceInerests
            callServer<bool> x "listUpdateMember" (Some p)

        let batchSubscribe (x: MCApi) (listID: string) (batch: Merges seq)
            (options: SubscribeOptions opt) =
            let p = Input()
            p.["id"] <- listID
            p.["batch"] <- batch
            match options with
            | None -> ()
            | Some o -> p.Merge o
            callServer<BatchSubscribe> x "listBatchSubscribe" (Some p)

        let batchUnsubscribe (x: MCApi) (listID: string) (batch: string seq)
            (options: UnsubscribeOptions opt) =
            let p = Input()
            p.["id"] <- listID
            p.["emails"] <- batch
            match options with
            | None -> ()
            | Some o -> p.Merge o
            callServer<BatchUnsubscribe> x "listBatchUnsubscribe" (Some p)

        //------------------------------------------------------------------------------
        //  (*) Webhooks
        //------------------------------------------------------------------------------

        let webhooks (x: MCApi) (listID: string) =
            let p = Input()
            p.["id"] <- listID
            callServer<MCList<Webhook>> x "listWebhooks" (Some p)

        let webhookAdd (x: MCApi) (listID: string) (url: string)
            (actions: WebhookActions opt) (sources: WebhookSources opt) =
            let p = Input()
            p.["id"] <- listID
            p.["url"] <- url
            p.["actions"] <- actions
            p.["sources"] <- sources
            callServer<bool> x "listWebhookAdd" (Some p)

        let webhookDel (x: MCApi) (listID: string) (url: string) =
            let p = Input()
            p.["id"] <- listID
            p.["url"] <- url
            callServer<bool> x "listWebhookDel" (Some p)

        //------------------------------------------------------------------------------
        //  (*) Merge Vars
        //------------------------------------------------------------------------------

        let mergeVars (x: MCApi) (listID: string) =
            let p = Input()
            p.["id"] <- listID
            callServer<MCList<MergeVar>> x "listMergeVars" (Some p)

        let private mergeVarOp (x: MCApi) op (listID: string) (tag: string)
            (name: string opt) (options: MergeVarOptions opt) =
            let p = Input()
            let n = "listMergeVar" + op
            p.["id"] <- listID
            p.["tag"] <- tag
            p.["name"] <- name
            p.["options"] <- options
            callServer<bool> x n (Some p)

        let mergeVarAdd x listID tag name options =
            mergeVarOp x "Add" listID tag (Some name) options

        let mergeVarDel x listID tag =
            mergeVarOp x "Del" listID tag None None

        let mergeVarUpdate x listID tag options =
            mergeVarOp x "Update" listID tag None (Some options)

        let mergeVarReset x listID tag =
            mergeVarOp x "Reset" listID tag None None

    //==================================================================================
    //  Campaign related methods
    //==================================================================================

    module Campaign =

        open MailChimp.Types.Campaign

        //------------------------------------------------------------------------------
        //  (*) Campaigns
        //------------------------------------------------------------------------------

        let campaigns (x: MCApi) (filters: Filter opt) (start: int opt) (limit: int opt) =
            let p = Input()
            p.["filters"] <- filters
            p.["start"] <- start
            p.["limit"] <- limit
            callServer<Campaigns> x "campaigns" (Some p)

        //------------------------------------------------------------------------------
        //  (*) Create/Delete
        //------------------------------------------------------------------------------

        let create (x: MCApi) (campaignType: Type) (options: Options) (content: Content.Base)
            (segmentOpts: SegmentOptions opt) (typeOpts: TypeOptions.Base opt) =
            let p = Input()
            p.["type"] <- campaignType
            p.["options"] <- options
            p.["content"] <- content
            p.["segment_opts"] <- segmentOpts
            p.["type_opts"] <- typeOpts
            callServer<string> x "campaignCreate" (Some p)

        let delete (x: MCApi) (campaignID: string) =
            let p = Input()
            p.["cid"] <- campaignID
            callServer<bool> x "campaignDelete" (Some p)

        let replicate (x: MCApi) (campaignID: string) =
            let p = Input()
            p.["cid"] <- campaignID
            callServer<string> x "campaignReplicate" (Some p)

        let update (x: MCApi) (campaignID: string) (name: string) (value: obj) =
            let p = Input()
            p.["cid"] <- campaignID
            p.["name"] <- name
            p.["value"] <- value
            callServer<bool> x "campaignUpdate" (Some p)

        //------------------------------------------------------------------------------
        //  (*) Send/Schedule
        //------------------------------------------------------------------------------

        let segmentTest (x: MCApi) (listID: string) (options: SegmentOptions) =
            let p = Input()
            p.["list_id"] <- listID
            p.["options"] <- options
            callServer<int> x "campaignSegmentTest" (Some p)

        let pause (x: MCApi) (campaignID: string) =
            let p = Input()
            p.["cid"] <- campaignID
            callServer<bool> x "campaignPause" (Some p)

        let resume (x: MCApi) (campaignID: string) =
            let p = Input()
            p.["cid"] <- campaignID
            callServer<bool> x "campaignResume" (Some p)

        let schedule (x: MCApi) (campaignID: string) (scheduleTime: DateTime)
            (scheduleTimeB: DateTime opt) =
            let p = Input()
            p.["cid"] <- campaignID
            p.["schedule_time"] <- scheduleTime
            p.["schedule_time_b"] <- scheduleTimeB
            callServer<bool> x "campaignSchedule" (Some p)

        let scheduleBatch (x: MCApi) (campaignID: string) (scheduleTime: DateTime)
            (numBatches: int opt) (staggerMins: int opt) =
            let p = Input()
            p.["cid"] <- campaignID
            p.["schedule_time"] <- scheduleTime
            p.["num_batches"] <- numBatches
            p.["stagger_mins"] <- staggerMins
            callServer<bool> x "campaignScheduleBatch" (Some p)

        let unschedule (x: MCApi) (campaignID: string) =
            let p = Input()
            p.["cid"] <- campaignID
            callServer<bool> x "campaignUnschedule" (Some p)

        let sendNow (x: MCApi) (campaignID: string) =
            let p = Input()
            p.["cid"] <- campaignID
            callServer<bool> x "campaignSendNow" (Some p)

        let sendTest (x: MCApi) (campaignID: string) (emails: string seq)
            (sendType: TestEmailType opt) =
            let p = Input()
            p.["cid"] <- campaignID
            p.["test_emails"] <- emails
            p.["send_type"] <- sendType
            callServer<bool> x "campaignSendTest" (Some p)

        //------------------------------------------------------------------------------
        //  (*) Misc
        //------------------------------------------------------------------------------

        let content (x: MCApi) (campaignID: string) (forArchive: bool opt) =
            let p = Input()
            p.["cid"] <- campaignID
            p.["forArchive"] <- forArchive
            callServer<Content.Html> x "campaignContent" (Some p)

        let templateContent (x: MCApi) (campaignID: string) =
            let tc = Content.Template()
            let p = Input()
            p.["cid"] <- campaignID
            callServer<MCDict<string>> x "campaignTemplateContent" (Some p) |> tc.Merge false
            tc

        let shareReport (x: MCApi) (campaignID: string)
            (options: ShareReportOptions opt) =
            let p = Input()
            p.["cid"] <- campaignID
            p.["opts"] <- options
            callServer<ShareReport> x "campaignShareReport" (Some p)

        let ecommOrderAdd (x: MCApi) (order: Ecommerce.Order) =
            let p = Input()
            p.["order"] <- order
            callServer<bool> x "campaignEcommOrderAdd" (Some p)

        //==================================================================================
        //  Campaign stats related methods
        //==================================================================================

        module Stats =

            open MailChimp.Types.Campaign.Stats

            let stats (x: MCApi) (campaignID: string) =
                let p = Input()
                p.["cid"] <- campaignID
                callServer<Stats> x "campaignStats" (Some p)

            let abuseReports (x: MCApi) (campaignID: string) (start: int opt)
                (limit: int opt) (since: DateTime opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["start"] <- start
                p.["limit"] <- limit
                p.["since"] <- since
                callServer<MCList<AbuseReport>> x "campaignAbuseReports" (Some p)

            let members (x: MCApi) (campaignID: string) (status: SendStatus opt)
                (start: int opt) (limit: int opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["status"] <- status
                p.["start"] <- start
                p.["limit"] <- limit
                callServer<Members> x "campaignMembers" (Some p)

            let advice (x: MCApi) (campaignID: string) =
                let p = Input()
                p.["cid"] <- campaignID
                callServer<MCList<Advice>> x "campaignAdvice" (Some p)

            let analytics (x: MCApi) (campaignID: string) =
                let p = Input()
                p.["cid"] <- campaignID
                callServer<Analytics> x "campaignAnalytics" (Some p)

            let bounceMessage (x: MCApi) (campaignID: string) (email: string) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["email"] <- email
                callServer<BounceMessage> x "campaignBounceMessage" (Some p)

            let bounceMessages (x: MCApi) (campaignID: string) (start: int opt)
                (limit: int opt) (since: DateTime opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["start"] <- start
                p.["limit"] <- limit
                p.["since"] <- since
                callServer<BounceMessages> x "campaignBounceMessages" (Some p)

            let clickStats (x: MCApi) (campaignID: string) =
                let p = Input()
                p.["cid"] <- campaignID
                callServer<MCDict<string, ClickStats>> x "campaignClickStats" (Some p)

            let eepUrlStats (x: MCApi) (campaignID: string) =
                let p = Input()
                p.["cid"] <- campaignID
                callServer<EepUrlStats> x "campaignEepUrlStats" (Some p)

            let emailDomainPerformance (x: MCApi) (campaignID: string) =
                let p = Input()
                p.["cid"] <- campaignID
                callServer<MCList<EmailDomainPerformance>> x "campaignEmailDomainPerformance" (Some p)

            let geoOpens (x: MCApi) (campaignID: string) =
                let p = Input()
                p.["cid"] <- campaignID
                callServer<MCList<GeoOpens>> x "campaignGeoOpens" (Some p)

            let geoOpensForCountry (x: MCApi) (campaignID: string) (countryCode: string) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["code"] <- countryCode
                callServer<MCList<GeoOpens>> x "campaignGeoOpensForCountry" (Some p)

            let unsubscribes (x: MCApi) (campaignID: string) (start: int opt) (limit: int opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["start"] <- start
                p.["limit"] <- limit
                callServer<Unsubscribes> x "campaignUnsubscribes" (Some p)

            let ecommOrders (x: MCApi) (campaignID: string) (start: int opt) (limit: int opt)
                (since: DateTime opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["start"] <- start
                p.["limit"] <- limit
                p.["since"] <- since
                callServer<Ecommerce.Orders> x "campaignEcommOrders" (Some p)

        //==================================================================================
        //  Campaign report data related methods
        //==================================================================================

        module ReportData =

            open MailChimp.Types.Campaign.ReportData

            let clickDetailAIM (x: MCApi) (campaignID: string) (url: string)
                (start: int opt) (limit: int opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["url"] <- url
                p.["start"] <- start
                p.["limit"] <- limit
                callServer<ClickDetailAIM> x "campaignClickDetailAIM" (Some p)

            let emailStatsAIM (x: MCApi) (campaignID: string) (emails: string seq) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["email_address"] <- emails
                callServerFn<EmailStatsAIM> x "campaignEmailStatsAIM" (Some p) (fun (y, s) ->
                  { y with ErrorsData = getEmailErrors (mcnull y.Errors) s })

            let emailStatsAIMAll (x: MCApi) (campaignID: string) (start: int opt) (limit: int opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["start"] <- start
                p.["limit"] <- limit
                callServer<EmailStatsAIMAll> x "campaignEmailStatsAIMAll" (Some p)

            let openedAIM (x: MCApi) (campaignID: string) (start: int opt) (limit: int opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["start"] <- start
                p.["limit"] <- limit
                callServer<OpenedAIM> x "campaignOpenedAIM" (Some p)

            let notOpenedAIM (x: MCApi) (campaignID: string) (start: int opt) (limit: int opt) =
                let p = Input()
                p.["cid"] <- campaignID
                p.["start"] <- start
                p.["limit"] <- limit
                callServer<NotOpenedAIM> x "campaignNotOpenedAIM" (Some p)

    //==================================================================================
    //  Folder related methods
    //==================================================================================

    module Folder =

        open MailChimp.Types.Folder

        let folders (x: MCApi) (folderType: Type opt) =
            let p = Input()
            p.["type"] <- folderType
            callServer<MCList<Folder>> x "folders" (Some p)

        let add (x: MCApi) (name: string) (folderType: Type opt) =
            let p = Input()
            p.["name"] <- name
            p.["type"] <- folderType
            callServer<int> x "folderAdd" (Some p)

        let del (x: MCApi) (folderID: int) (folderType: Type opt) =
            let p = Input()
            p.["fid"] <- folderID
            p.["type"] <- folderType
            callServer<bool> x "folderDel" (Some p)

        let update (x: MCApi) (folderID: int) (name: string) (folderType: Type opt) =
            let p = Input()
            p.["fid"] <- folderID
            p.["name"] <- name
            p.["type"] <- folderType
            callServer<bool> x "folderUpdate" (Some p)

    //==================================================================================
    //  Template related methods
    //==================================================================================

    module Template =

        open MailChimp.Types.Template

        let templates (x: MCApi) (templateType: Types opt)
            (inactives: Inactives opt) (category: string opt) =
            let p = Input()
            p.["types"] <- templateType
            p.["inactives"] <- inactives
            p.["category"] <- category
            callServer<Templates> x "templates" (Some p)

        let add (x: MCApi) (name: string) (html: string) =
            let p = Input()
            p.["name"] <- name
            p.["html"] <- html
            callServer<int> x "templateAdd" (Some p)

        let del (x: MCApi) (templateID: int) =
            let p = Input()
            p.["id"] <- templateID
            callServer<bool> x "templateDel" (Some p)

        let undel (x: MCApi) (templateID: int) =
            let p = Input()
            p.["id"] <- templateID
            callServer<bool> x "templateUndel" (Some p)

        let info (x: MCApi) (templateID: int) (templateType: Type opt) =
            let p = Input()
            p.["tid"] <- templateID
            p.["type"] <- templateType
            callServer<Info> x "templateInfo" (Some p)

        let update (x: MCApi) (templateID: int) (name: string opt) (html: string opt) =
            let v = Input()
            v.["name"] <- name
            v.["html"] <- html
            let p = Input()
            p.["id"] <- templateID
            p.["values"] <- v
            callServer<bool> x "templateUpdate" (Some p)

    //==================================================================================
    //  Ecommerce methods
    //==================================================================================

    module Ecommerce =

        open MailChimp.Types.Ecommerce

        let orders (x: MCApi) (start: int opt) (limit: int opt)
            (since: DateTime opt) =
            let p = Input()
            p.["start"] <- start
            p.["limit"] <- limit
            p.["since"] <- since
            callServer<Orders> x "ecommOrders" (Some p)

        let orderAdd (x: MCApi) (order: Order) =
            let p = Input()
            p.["order"] <- order
            callServer<bool> x "ecommOrderAdd" (Some p)

        let orderDel (x: MCApi) (storeID: string) (orderID: string) =
            let p = Input()
            p.["store_id"] <- storeID
            p.["order_id"] <- orderID
            callServer<bool> x "ecommOrderDel" (Some p)

    //==================================================================================
    //  Golden Monkeys methods
    //==================================================================================

    module GoldenMonkeys =

        open MailChimp.Types.GoldenMonkeys

        let members (x: MCApi) =
            callServer<MCList<Member>> x "gmonkeyMembers" None

        let activity (x: MCApi) =
            callServer<MCList<MemberAction>> x "gmonkeyActivity" None

        let private op (x: MCApi) op (listID: string) (emails: string seq) =
            let p = Input()
            let n = "gmonkey" + op
            p.["id"] <- listID
            p.["email_address"] <- emails
            callServer<MemberOpResult> x n (Some p)

        let add x listID emails = op x "Add" listID emails
        let del x listID emails = op x "Del" listID emails


//==================================================================================
//  MCApi augmentation
//==================================================================================

module MS = MCApi.Security
module MC = MCApi.Campaign
module ML = MCApi.List
module MF = MCApi.Folder
module MT = MCApi.Template
module ME = MCApi.Ecommerce
module MH = MCApi.Helper
module MG = MCApi.GoldenMonkeys

type MCApi with

    //------------------------------------------------------------------------------
    //  (*) Custom API call
    //------------------------------------------------------------------------------

    member x.CallServer<'T>(methodName, [<op>]p) = MCApi.callServer<'T> x methodName p

    //==================================================================================
    //  Security related methods
    //==================================================================================

    member x.ApiKeys(user, pwd, [<op>]expired) = MS.apikeys x user pwd expired
    member x.ApiKeys(user, pwd, ?expired) = MS.apikeys x user pwd ((<?)expired)
    member x.ApiKeyAdd(user, pwd) = MS.apikeyAdd x user pwd
    member x.ApiKeyExpire(user, pwd) = MS.apikeyExpire x user pwd

    //==================================================================================
    //  Helper methods
    //==================================================================================

    member x.Ping() = MH.ping x
    member x.GetAccountDetails([<ParamArray>]exclude) = MH.getAccountDetails x exclude
    member x.GetVerifiedDomains() = MH.getVerifiedDomains x
    member x.ChimpChatter() = MH.chimpChatter x
    member x.ListsForEmail(email) = MH.listsForEmail x email
    member x.CampaignsForEmail(email, [<op>]listID) = MH.campaignsForEmail x email listID
    member x.CampaignsForEmail(email, ?listID) = MH.campaignsForEmail x email ((<?)listID)
    member x.CampaignsForEmail2(email, [<op>]listID) = MH.campaignsForEmail2 x email listID
    member x.CampaignsForEmail2(email, ?listID) = MH.campaignsForEmail2 x email ((<?)listID)
    member x.InlineCss(html, [<op>]stripCss) = MH.inlineCss x html stripCss
    member x.InlineCss(html, ?stripCss) = MH.inlineCss x html ((<?)stripCss)
    member x.GenerateText(contentType, content: obj) = MH.generateText x contentType content
    member x.SearchMembers(query, [<op>]listID, [<op>]offset) =
        MH.searchMembers x query listID  offset
    member x.SearchMembers(query, ?listID, ?offset) =
        MH.searchMembers x query ((<?)listID) ((<?)offset)
    member x.SearchCampaigns(query, [<op>]offset, [<op>]snipRange) =
        MH.searchCampaigns x query offset snipRange
    member x.SearchCampaigns(query, ?offset, ?snipRange) =
        MH.searchCampaigns x query ((<?)offset) ((<?)snipRange)

    //==================================================================================
    //  List related methods
    //==================================================================================

    //------------------------------------------------------------------------------
    //  (*) Lists
    //------------------------------------------------------------------------------

    member x.Lists([<op>]filters, [<op>]start, [<op>]limit, [<op>]sortField, [<op>]sortDir) =
        ML.lists x filters start limit sortField sortDir
    member x.Lists(?filters, ?start, ?limit, ?sortField, ?sortDir) =
        ML.lists x ((<?)filters) ((<?)start) ((<?)limit) ((<?)sortField) ((<?)sortDir)

    //------------------------------------------------------------------------------
    //  (*) Activity
    //------------------------------------------------------------------------------

    member x.ListActivity(listID) = ML.activity x listID
    member x.ListLocations(listID) = ML.locations x listID
    member x.ListAbuseReports(listID, [<op>]start, [<op>]limit, [<op>]since) =
        ML.abuseReports x listID start limit since
    member x.ListAbuseReports(listID, ?start, ?limit, ?since) =
        ML.abuseReports x listID ((<?)start) ((<?)limit) ((<?)since)
    member x.ListGrowthHistory(listID) = ML.growthHistory x listID
    member x.ListClients(listID) = ML.clients x listID

    //------------------------------------------------------------------------------
    //  (*) Interest Groupings/Groups
    //------------------------------------------------------------------------------

    member x.ListInterestGroupings(listID) = ML.interestGroupings x listID
    member x.ListInterestGroupingAdd(listID, name, groupingType, groups) =
        ML.interestGroupingAdd x listID name groupingType groups
    member x.ListInterestGroupingDel(groupingID) = ML.interestGroupingDel x groupingID
    member x.ListInterestGroupingUpdate(groupingID, value: obj) =
        ML.interestGroupingUpdate x groupingID value
    member x.ListInterestGroupAdd(listID, name, [<op>]groupingID) =
        ML.interestGroupAdd x listID name groupingID
    member x.ListInterestGroupAdd(listID, name, ?groupingID) =
        ML.interestGroupAdd x listID name ((<?)groupingID)
    member x.ListInterestGroupDel(listID, name, [<op>]groupingID) =
        ML.interestGroupDel x listID name groupingID
    member x.ListInterestGroupDel(listID, name, ?groupingID) =
        ML.interestGroupDel x listID name ((<?)groupingID)
    member x.ListInterestGroupUpdate(listID, oldName, newName, [<op>]groupingID) =
        ML.interestGroupUpdate x listID oldName newName groupingID
    member x.ListInterestGroupUpdate(listID, oldName, newName, ?groupingID) =
        ML.interestGroupUpdate x listID oldName newName ((<?)groupingID)

    //------------------------------------------------------------------------------
    //  (*) Static Segments
    //------------------------------------------------------------------------------

    member x.ListStaticSegments(listID) = ML.staticSegments x listID
    member x.ListStaticSegmentAdd(listID, name) = ML.staticSegmentAdd x listID name
    member x.ListStaticSegmentDel(listID, segID) = ML.staticSegmentDel x listID segID
    member x.ListStaticSegmentReset(listID, segID) = ML.staticSegmentReset x listID segID
    member x.ListStaticSegmentMembersAdd(listID, segID, batch) =
        ML.staticSegmentMembersAdd x listID segID batch
    member x.ListStaticSegmentMembersDel(listID, segID, batch) =
        ML.staticSegmentMembersDel x listID segID batch

    //------------------------------------------------------------------------------
    //  (*) Members/Info/Activity
    //------------------------------------------------------------------------------

    member x.ListMembers(listID, status, [<op>]since, [<op>]start, [<op>]limit, [<op>]sortDir) =
        ML.members x listID status since start limit sortDir
    member x.ListMembers(listID, status, ?since, ?start, ?limit, ?sortDir) =
        ML.members x listID status ((<?)since) ((<?)start) ((<?)limit) ((<?)sortDir)
    member x.ListMemberInfo(listID, email) = ML.memberInfo x listID [email]
    member x.ListMemberInfo(listID, emails) = ML.memberInfo x listID emails
    member x.ListMemberActivity(listID, email) = ML.memberActivity x listID [email]
    member x.ListMemberActivity(listID, emails) = ML.memberActivity x listID emails

    //------------------------------------------------------------------------------
    //  (*) Subscribe/Unsubscribe
    //------------------------------------------------------------------------------

    member x.ListSubscribe(listID, email, [<op>]merges, [<op>]options) =
        ML.subscribe x listID email merges options
    member x.ListSubscribe(listID, email, ?merges, ?options) =
        ML.subscribe x listID email ((<?)merges) ((<?)options)
    member x.ListUnsubscribe(listID, email, [<op>]options) =
        ML.unsubscribe x listID email options
    member x.ListUnsubscribe(listID, email, ?options) =
        ML.unsubscribe x listID email ((<?)options)
    member x.ListUpdateMember(listID, email, merges, [<op>]emailType, [<op>]replaceInterests) =
        ML.updateMember x listID email merges emailType replaceInterests
    member x.ListUpdateMember(listID, email, merges, ?emailType, ?replaceInterests) =
        ML.updateMember x listID email merges ((<?)emailType) ((<?)replaceInterests)
    member x.ListBatchSubscribe(listID, batch, [<op>]options) =
        ML.batchSubscribe x listID batch options
    member x.ListBatchSubscribe(listID, batch, ?options) =
        ML.batchSubscribe x listID batch ((<?)options)
    member x.ListBatchUnsubscribe(listID, batch, [<op>]options) =
        ML.batchUnsubscribe x listID batch options
    member x.ListBatchUnsubscribe(listID, batch, ?options) =
        ML.batchUnsubscribe x listID batch ((<?)options)

    //------------------------------------------------------------------------------
    //  (*) Webhooks
    //------------------------------------------------------------------------------

    member x.ListWebhooks(listID) = ML.webhooks x listID
    member x.ListWebhookAdd(listID, url, [<op>]actions, [<op>]sources) =
        ML.webhookAdd x listID url actions sources
    member x.ListWebhookAdd(listID, url, ?actions, ?sources) =
        ML.webhookAdd x listID url ((<?)actions) ((<?)sources)
    member x.ListWebhookDel(listID, url) = ML.webhookDel x listID url

    //------------------------------------------------------------------------------
    //  (*) Merge Vars
    //------------------------------------------------------------------------------

    member x.ListMergeVars(listID) = ML.mergeVars x listID
    member x.ListMergeVarAdd(listID, tag, name, [<op>]options) =
        ML.mergeVarAdd x listID tag name options
    member x.ListMergeVarAdd(listID, tag, name, ?options) =
        ML.mergeVarAdd x listID tag name ((<?)options)
    member x.ListMergeVarDel(listID, tag) = ML.mergeVarDel x listID tag
    member x.ListMergeVarUpdate(listID, tag, options) =
        ML.mergeVarUpdate x listID tag options
    member x.ListMergeVarReset(listID, tag) = ML.mergeVarReset x listID tag

    //==================================================================================
    //  Campaign related methods
    //==================================================================================

    //------------------------------------------------------------------------------
    //  (*) Campaigns
    //------------------------------------------------------------------------------

    member x.Campaigns([<op>]filters, [<op>]start, [<op>]limit) =
        MC.campaigns x filters start limit
    member x.Campaigns(?filters, ?start, ?limit) =
        MC.campaigns x ((<?)filters) ((<?)start) ((<?)limit)

    //------------------------------------------------------------------------------
    //  (*) Create/Delete
    //------------------------------------------------------------------------------

    member x.CampaignCreate(campaignType, options, content, [<op>]segmentOpts, [<op>]typeOpts) =
        MC.create x campaignType options content segmentOpts typeOpts
    member x.CampaignCreate(campaignType, options, content, ?segmentOpts, ?typeOpts) =
        MC.create x campaignType options content  ((<?)segmentOpts) ((<?)typeOpts)
    member x.CampaignDelete(campaignID) = MC.delete x campaignID
    member x.CampaignReplicate(campaignID) = MC.replicate x campaignID
    member x.CampaignUpdate(campaignID, name, value: obj) = MC.update x campaignID name value

    //------------------------------------------------------------------------------
    //  (*) Send/Schedule
    //------------------------------------------------------------------------------

    member x.CampaignSegmentTest(listID, options) = MC.segmentTest x listID options
    member x.CampaignPause(campaignID) = MC.pause x campaignID
    member x.CampaignResume(campaignID) = MC.resume x campaignID
    member x.CampaignSchedule(campaignID, scheduleTime, [<op>]scheduleTimeB) =
        MC.schedule x campaignID scheduleTime scheduleTimeB
    member x.CampaignSchedule(campaignID, scheduleTime, ?scheduleTimeB) =
        MC.schedule x campaignID scheduleTime ((<?)scheduleTimeB)
    member x.CampaignScheduleBatch(campaignID, scheduleTime, [<op>]numBatches, [<op>]staggerMins) =
        MC.scheduleBatch x campaignID scheduleTime numBatches staggerMins
    member x.CampaignScheduleBatch(campaignID, scheduleTime, ?numBatches, ?staggerMins) =
        MC.scheduleBatch x campaignID scheduleTime ((<?)numBatches) ((<?)staggerMins)
    member x.CampaignUnschedule(campaignID) = MC.unschedule x campaignID
    member x.CampaignSendNow(campaignID) = MC.sendNow x campaignID
    member x.CampaignSendTest(campaignID, emails, [<op>]sendType) =
        MC.sendTest x campaignID emails sendType
    member x.CampaignSendTest(campaignID, emails, ?sendType) =
        MC.sendTest x campaignID emails ((<?)sendType)

    //------------------------------------------------------------------------------
    //  (*) Misc
    //------------------------------------------------------------------------------

    member x.CampaignContent(campaignID, [<op>]forArchive) = MC.content x campaignID forArchive
    member x.CampaignContent(campaignID, ?forArchive) = MC.content x campaignID ((<?)forArchive)
    member x.CampaignTemplateContent(campaignID) = MC.templateContent x campaignID
    member x.CampaignShareReport(campaignID, [<op>]options) = MC.shareReport x campaignID options
    member x.CampaignShareReport(campaignID, ?options) = MC.shareReport x campaignID ((<?)options)
    member x.CampaignEcommOrderAdd(order) = MC.ecommOrderAdd x order

    //==================================================================================
    //  Campaign stats related methods
    //==================================================================================

    member x.CampaignStats(campaignID) = MC.Stats.stats x campaignID
    member x.CampaignAbuseReports(campaignID, [<op>]start, [<op>]limit, [<op>]since) =
        MC.Stats.abuseReports x campaignID start limit since
    member x.CampaignAbuseReports(campaignID, ?start, ?limit, ?since) =
        MC.Stats.abuseReports x campaignID ((<?)start) ((<?)limit) ((<?)since)
    member x.CampaignMembers(campaignID, [<op>]status, [<op>]start, [<op>]limit) =
        MC.Stats.members x campaignID status start limit
    member x.CampaignMembers(campaignID, ?status, ?start, ?limit) =
        MC.Stats.members x campaignID ((<?)status) ((<?)start) ((<?)limit)
    member x.CampaignAdvice(campaignID) = MC.Stats.advice x campaignID
    member x.CampaignAnalytics(campaignID) = MC.Stats.analytics x campaignID
    member x.CampaignBounceMessage(campaignID, email) =
        MC.Stats.bounceMessage x campaignID email
    member x.CampaignBounceMessages(campaignID, [<op>]start, [<op>]limit, [<op>]since) =
        MC.Stats.bounceMessages x campaignID start limit since
    member x.CampaignBounceMessages(campaignID, ?start, ?limit, ?since) =
        MC.Stats.bounceMessages x campaignID ((<?)start) ((<?)limit) ((<?)since)
    member x.CampaignClickStats(campaignID) = MC.Stats.clickStats x campaignID
    member x.CampaignEepUrlStats(campaignID) = MC.Stats.eepUrlStats x campaignID
    member x.CampaignEmailDomainPerformance(campaignID) =
        MC.Stats.emailDomainPerformance x campaignID
    member x.CampaignGeoOpens(campaignID) = MC.Stats.geoOpens x campaignID
    member x.CampaignGeoOpensForCountry(campaignID, countryCode) =
        MC.Stats.geoOpensForCountry x campaignID countryCode
    member x.CampaignUnsubscribes(campaignID, [<op>]start, [<op>]limit) =
        MC.Stats.unsubscribes x campaignID start limit
    member x.CampaignUnsubscribes(campaignID, ?start, ?limit) =
        MC.Stats.unsubscribes x campaignID ((<?)start) ((<?)limit)
    member x.CampaignEcommOrders(campaignID, [<op>]start, [<op>]limit, [<op>]since) =
        MC.Stats.ecommOrders x campaignID start limit since
    member x.CampaignEcommOrders(campaignID, ?start, ?limit, ?since) =
        MC.Stats.ecommOrders x campaignID ((<?)start) ((<?)limit) ((<?)since)

    //==================================================================================
    //  Campaign report data related methods
    //==================================================================================

    member x.CampaignClickDetailAIM(campaignID, url, [<op>]start, [<op>]limit) =
        MC.ReportData.clickDetailAIM x campaignID url start limit
    member x.CampaignClickDetailAIM(campaignID, url, ?start, ?limit) =
        MC.ReportData.clickDetailAIM x campaignID url ((<?)start) ((<?)limit)
    member x.CampaignEmailStatsAIM(campaignID, emails) =
        MC.ReportData.emailStatsAIM x campaignID emails
    member x.CampaignEmailStatsAIMAll(campaignID, [<op>]start, [<op>]limit) =
        MC.ReportData.emailStatsAIMAll x campaignID start limit
    member x.CampaignEmailStatsAIMAll(campaignID, ?start, ?limit) =
        MC.ReportData.emailStatsAIMAll x campaignID ((<?)start) ((<?)limit)
    member x.CampaignOpenedAIM(campaignID, [<op>]start, [<op>]limit) =
        MC.ReportData.openedAIM x campaignID start limit
    member x.CampaignOpenedAIM(campaignID, ?start, ?limit) =
        MC.ReportData.openedAIM x campaignID ((<?)start) ((<?)limit)
    member x.CampaignNotOpenedAIM(campaignID, [<op>]start, [<op>]limit) =
        MC.ReportData.notOpenedAIM x campaignID start limit
    member x.CampaignNotOpenedAIM(campaignID, ?start, ?limit) =
        MC.ReportData.notOpenedAIM x campaignID ((<?)start) ((<?)limit)

    //==================================================================================
    //  Folder related methods
    //==================================================================================

    member x.Folders([<op>]folderType) = MF.folders x folderType
    member x.Folders(?folderType) = MF.folders x ((<?)folderType)
    member x.FolderAdd(name, [<op>]folderType) = MF.add x name folderType
    member x.FolderAdd(name, ?folderType) = MF.add x name ((<?)folderType)
    member x.FolderDel(folderID, [<op>]folderType) = MF.del x folderID folderType
    member x.FolderDel(folderID, ?folderType) = MF.del x folderID ((<?)folderType)
    member x.FolderUpdate(folderID, name, [<op>]folderType) =
        MF.update x folderID name folderType
    member x.FolderUpdate(folderID, name, ?folderType) =
        MF.update x folderID name ((<?)folderType)

    //==================================================================================
    //  Template related methods
    //==================================================================================

    member x.Templates([<op>]templateTypes, [<op>]inactives, [<op>]category) =
        MT.templates x templateTypes inactives category
    member x.Templates(?templateTypes, ?inactives, ?category) =
        MT.templates x ((<?)templateTypes) ((<?)inactives) ((<?)category)
    member x.TemplateAdd(name, html) = MT.add x name html
    member x.TemplateDel(templateID) = MT.del x templateID
    member x.TemplateUndel(templateID) = MT.undel x templateID
    member x.TemplateInfo(templateID, [<op>]templateType) = MT.info x templateID templateType
    member x.TemplateInfo(templateID, ?templateType) = MT.info x templateID ((<?)templateType)
    member x.TemplateUpdate(templateID, [<op>]name, [<op>]html) = MT.update x templateID name html
    member x.TemplateUpdate(templateID, ?name, ?html) = MT.update x templateID ((<?)name) ((<?)html)

    //==================================================================================
    //  Ecommerce methods
    //==================================================================================

    member x.EcommOrders([<op>]start, [<op>]limit, [<op>]since) = ME.orders x start limit since
    member x.EcommOrders(?start, ?limit, ?since) = ME.orders x ((<?)start) ((<?)limit) ((<?)since)
    member x.EcommOrderAdd(order) = ME.orderAdd x order
    member x.EcommOrderDel(storeID, orderID) = ME.orderDel x storeID orderID

    //==================================================================================
    //  Golden Monkeys methods
    //==================================================================================

    member x.GmonkeyMembers() = MG.members x
    member x.GmonkeyActivity() = MG.activity x
    member x.GmonkeyAdd(listID, emails) = MG.add x listID emails
    member x.GmonkeyDel(listID, emails) = MG.del x listID emails